package com.sudo.api.mybatis.interceptor;

import com.sudo.common.annotation.mybatis.*;
import com.sudo.common.constant.UserConstant;
import com.sudo.common.exception.BizException;
import com.sudo.common.utils.HttpServletUtil;
import com.sudo.common.utils.HttpUserInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @Author：DayDayUp
 * @Create：2023/3/20 13:49
 */
@Slf4j
@Component
@Intercepts({
        @Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class})
})
public class BaseEntityInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取 SQL 命令
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        //只判断新增和修改
        if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
            // 获取参数
            Object parameter = invocation.getArgs()[1];
            // 获取当前sessionUser
            String sessionUser;
            HttpServletRequest request = HttpServletUtil.getRequest();
            if (request != null) {
                sessionUser = HttpUserInfoUtil.getSessionUser(request);
            }else {
                sessionUser = UserConstant.SYSTEM;
            }
            LocalDateTime now = LocalDateTime.now();
            //批量操作时
            if (parameter instanceof MapperMethod.ParamMap) {
                MapperMethod.ParamMap map = (MapperMethod.ParamMap) parameter;
                boolean hasKeyList = map.containsKey("list");
                if (hasKeyList) {
                    Object obj = map.get("list");
                    List<?> list = (List<?>) obj;
                    for (Object o : list) {
                        setParameter(o, sqlCommandType,sessionUser,now);
                    }
                }else {
                    Object re = map.get("record");
                    setParameter(re, sqlCommandType,sessionUser,now);
                }
            }else {
                setParameter(parameter, sqlCommandType,sessionUser,now);
            }
        }
        return invocation.proceed();
    }

    private void setParameter(Object parameter, SqlCommandType sqlCommandType, String sessionUser,LocalDateTime now) throws IllegalAccessException {
        Class<?> aClass = parameter.getClass();

        List<Field> fieldList = new ArrayList<>();
        fieldList.addAll(Arrays.asList(aClass.getDeclaredFields()));
        fieldList.addAll(Arrays.asList(aClass.getFields()));

        for (Field field : fieldList) {
            // 插入
            if (SqlCommandType.INSERT.equals(sqlCommandType)) {
                if (field.getAnnotation(CreateBy.class) != null) {
                    field.setAccessible(true);
                    field.set(parameter, sessionUser);
                }
                // insert 语句插入 createTime
                if (field.getAnnotation(CreateTime.class) != null) {
                    field.setAccessible(true);
                    if (!field.getType().equals(LocalDateTime.class)) {
                        throw new BizException("@CreateTime修饰的属性必须采用LocalDateTime");
                    } else {
                        field.set(parameter,now);
                    }
                }
            }
            // 修改
            if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
                if (field.getAnnotation(UpdateBy.class) != null) {
                    field.setAccessible(true);
                    field.set(parameter, sessionUser);
                }
                // update 语句插入 updateTime
                if (field.getAnnotation(UpdateTime.class) != null) {
                    field.setAccessible(true);
                    if (!field.getType().equals(LocalDateTime.class)) {
                        throw new BizException("@UpdateTime修饰的属性必须采用LocalDateTime");
                    } else {
                        field.set(parameter,now);
                    }
                }
            }
        }
    }

    /**
     * 这里可写可不写，看看父类方法就知道
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        //获取代理权
        if (target instanceof Executor) {
            //如果是Executor（执行增删改查操作），则拦截下来
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
